fix: Clarity 초기화 위치 수정 및 그룹 스터디 수정 폼 초기값 안정화#387
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthrough레이아웃에서 직접 Microsoft Clarity 초기화를 제거하고 재사용 가능한 Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
- 서버 레이아웃의 Clarity.init()를 클라이언트 전용 컴포넌트로 이동 - service/landing 레이아웃에 ClarityInit 적용 - edit 모드에서 데이터 로딩 전 GroupStudyForm 마운트 방지 - phone verification store의 persist partialize/onRehydrateStorage 타입 명시 - 전화번호 인증 동기화 시 memberId 저장/반영 로직 보강
eded448 to
51b2151
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/features/phone-verification/model/use-phone-verification-status.ts (1)
147-196:⚠️ Potential issue | 🔴 Critical병합 충돌 마커가 해결되지 않았습니다.
useEffect내 서버→스토어 동기화 로직과 의존성 배열에 충돌 마커가 남아 있어 CI가 실패합니다."Updated upstream" 브랜치가
resolvedServerIsVerified를 사용하고usePhoneVerificationStore.getState()로 현재 상태를 가져오는 더 완성된 구현으로 보입니다. 반면 "Stashed changes"는 존재하지 않는zustandIsVerified,zustandPhoneNumber변수를 참조하고 있어 해당 부분을 사용하면 런타임 에러가 발생합니다.🐛 충돌 해결 제안 (Updated upstream 기준)
// 서버 → 스토어 동기화 useEffect(() => { if (isProfileLoading || !userProfile || !memberId) return; -<<<<<<< Updated upstream const current = usePhoneVerificationStore.getState(); if (resolvedServerIsVerified) { // 서버가 인증됨 → 스토어 갱신 (memberId 포함) if ( !current.isVerified || current.memberId !== memberId || current.phoneNumber !== serverPhoneNumber ) { store.setVerified(serverPhoneNumber ?? '', memberId); -||||||| Stash base - if (serverPhoneNumber) { - // 서버에 전화번호가 있으면 Zustand도 동기화 - if (!zustandIsVerified || zustandPhoneNumber !== serverPhoneNumber) { - setVerified(serverPhoneNumber); -======= - if (serverPhoneNumber) { - // 서버에 전화번호가 있으면 Zustand도 동기화 - if (!zustandIsVerified || zustandPhoneNumber !== serverPhoneNumber) { - setVerified(serverPhoneNumber, memberId ?? undefined); ->>>>>>> Stashed changes } } else { // 서버가 미인증 → 같은 유저 캐시 제거 if (current.isVerified && current.memberId === memberId) { store.reset(); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [ isProfileLoading, userProfile, memberId, serverIsVerified, -<<<<<<< Updated upstream serverPhoneNumber, resolvedServerIsVerified, -||||||| Stash base - zustandIsVerified, - zustandPhoneNumber, - setVerified, - reset, -======= - zustandIsVerified, - zustandPhoneNumber, - setVerified, - reset, - memberId, ->>>>>>> Stashed changes ]);src/features/phone-verification/model/store.ts (1)
7-114:⚠️ Potential issue | 🔴 Critical파일 전체에 병합 충돌 마커가 해결되지 않았습니다.
이 파일에 여러 개의 병합 충돌 블록이 있어 CI 파이프라인이 실패합니다:
- Lines 7-21:
PhoneVerificationState인터페이스 정의- Lines 35-69: 초기 상태 및
setVerified구현- Lines 74-85:
reset구현- Lines 90-114: persist 설정
두 브랜치 모두
verifiedAt,memberId,hasHydrated를 추가하는 동일한 목표를 가지고 있습니다. 주요 차이점:
verifiedAt타입:string | nullvsDate | null- 서버 동기화를 위해string(ISO 형식)이 더 적합합니다.setVerified: spread 연산자 vs state 콜백 - state 콜백이 기존 memberId를 유지하므로 더 안전합니다.🐛 충돌 해결 제안
interface PhoneVerificationState { isVerified: boolean; phoneNumber: string | null; -<<<<<<< Updated upstream verifiedAt: string | null; memberId: number | null; hasHydrated: boolean; setVerified: (phoneNumber: string, memberId?: number) => void; -||||||| Stash base - verifiedAt: Date | null; - setVerified: (phoneNumber: string) => void; -======= - verifiedAt: string | null; - memberId: number | null; - hasHydrated: boolean; - setVerified: (phoneNumber: string, memberId?: number) => void; - setHasHydrated: (hasHydrated: boolean) => void; ->>>>>>> Stashed changes reset: () => void; setHasHydrated: (hasHydrated: boolean) => void; } export const usePhoneVerificationStore = create<PhoneVerificationState>()( persist( (set) => ({ isVerified: false, -<<<<<<< Updated upstream - phoneNumber: null as string | null, - verifiedAt: null as string | null, - memberId: null as number | null, - hasHydrated: false, - setVerified: (phoneNumber, memberId) => - set({ -||||||| Stash base - phoneNumber: '', - verifiedAt: new Date(), - setVerified: (phoneNumber) => - set({ -======= phoneNumber: null, verifiedAt: null, memberId: null, hasHydrated: false, setVerified: (phoneNumber, memberId) => set((state) => ({ ->>>>>>> Stashed changes isVerified: true, phoneNumber, -<<<<<<< Updated upstream - verifiedAt: new Date().toISOString(), - ...(memberId !== undefined ? { memberId } : {}), -||||||| Stash base - verifiedAt: new Date(), -======= verifiedAt: new Date().toISOString(), memberId: memberId ?? state.memberId, })), - setHasHydrated: (hasHydrated) => - set({ - hasHydrated, ->>>>>>> Stashed changes - }), reset: () => set({ isVerified: false, -<<<<<<< Updated upstream - phoneNumber: null as string | null, - verifiedAt: null as string | null, - memberId: null as number | null, -||||||| Stash base - phoneNumber: null, - verifiedAt: null, -======= phoneNumber: null, verifiedAt: null, memberId: null, ->>>>>>> Stashed changes }), setHasHydrated: (hasHydrated) => set({ hasHydrated }), }), { -<<<<<<< Updated upstream - name: 'phone-verification-storage', - partialize: (state) => ({ -||||||| Stash base - name: 'phone-verification-storage', // 로컬 스토리지에 저장 -======= name: 'phone-verification-storage', partialize: (state): PersistedPhoneVerificationState => ({ ->>>>>>> Stashed changes isVerified: state.isVerified, phoneNumber: state.phoneNumber, verifiedAt: state.verifiedAt, memberId: state.memberId, }), onRehydrateStorage: () => (state) => { state?.setHasHydrated(true); }, }, ), );추가 참고사항:
null as string | null형태의 타입 단언은 불필요합니다. TypeScript가null을 자동으로 추론합니다.setVerified에서state콜백을 사용하는 "Stashed changes" 방식이 기존memberId를 보존하므로 더 안전합니다.
🤖 Fix all issues with AI agents
In `@src/app/`(landing)/layout.tsx:
- Around line 64-76: Remove the leftover git conflict markers (<<<<<<<, =======,
>>>>>>>) in layout.tsx and resolve the duplicated Clarity init block so the file
parses; ensure only the server-safe code remains (keep the await
getServerCookie('accessToken') call inside the server component) and move any
browser-only Clarity.init(CLARITY_PROJECT_ID) call out of the server component
(or guard it so it only runs in a client component/effect), referencing
getServerCookie, CLARITY_PROJECT_ID, and Clarity.init to locate the conflicting
sections; after cleanup verify no direct window/Clarity access remains in the
server component and that Clarity initialization is performed from a client-only
place.
In `@src/app/`(service)/layout.tsx:
- Around line 36-48: Remove the leftover Git merge-conflict markers (<<<<<<,
=======, >>>>>>>) from layout.tsx and resolve the conflict by keeping the
intended branch: ensure the server-side retrieval const initialAccessToken =
await getServerCookie('accessToken'); remains, retain the client-only Clarity
initialization block (if (typeof window !== 'undefined' && CLARITY_PROJECT_ID) {
Clarity.init(CLARITY_PROJECT_ID); }), and keep the GlobalToast-related JSX/logic
if that was selected; after updating the body, remove any now-unused imports
(e.g., Clarity, GlobalToast, getServerCookie) or add missing ones so the file
compiles cleanly.
In `@src/features/phone-verification/model/use-phone-auth-mutation.ts`:
- Around line 42-62: Remove the leftover Git conflict markers (<<<<<<<, |||||||,
=======, >>>>>>>) and consolidate the duplicated blocks into a single
implementation: compute currentMemberId using memberId ??
Number(getCookie('memberId')) and call setVerified(variables.phoneNumber,
currentMemberId || undefined); ensure only one copy of that logic remains and no
conflict markers remain in the file.
In `@src/features/study/group/ui/group-study-form-modal.tsx`:
- Around line 270-275: The conditional rendering uses an undefined isLoading
variable; replace it with the correct loading state(s) — e.g., use the combined
flags isGroupStudyLoading || isVerificationLoading in the Modal.Body check
(where mode === 'edit' is evaluated) so the block shows while either
isGroupStudyLoading or isVerificationLoading is true; ensure the other
conditional that uses editDefaultValues remains unchanged.
- Around line 102-109: There are unresolved Git merge conflict markers around
the guard clause; remove the markers (`<<<<<<<`, `|||||||`, `=======`,
`>>>>>>>`) and leave a single, correct guard or no guard depending on intent: if
you need the early return keep the guard using the loaded state variable used
elsewhere (use isGroupStudyLoading, not isLoading) as the condition in the guard
clause in function/component containing the lines with
isGroupStudyLoading/isLoading; otherwise remove the entire guard line so
conditional rendering later (lines ~241-244) handles it. Ensure only one of
isGroupStudyLoading or no guard remains and run a quick build to verify parsing
errors are gone.
🧹 Nitpick comments (1)
src/features/study/group/ui/group-study-form-modal.tsx (1)
264-280: edit 모드에서 데이터 로딩 실패 시 fallback UI가 없습니다.
isGroupStudyLoading이 false이고editDefaultValues도 null/undefined인 경우(데이터 로딩 실패 또는groupStudyInfo가 없는 경우), 모달 본문이 비어 보이게 됩니다.♻️ 에러 상태 처리 추가 제안
{mode === 'edit' && isGroupStudyLoading && ( <Modal.Body className="font-designer-16m text-text-subtle py-800 text-center"> 스터디 정보를 불러오는 중입니다... </Modal.Body> )} + {mode === 'edit' && !isGroupStudyLoading && !editDefaultValues && ( + <Modal.Body className="font-designer-16m text-text-subtle py-800 text-center"> + 스터디 정보를 불러오지 못했습니다. + </Modal.Body> + )} {mode === 'edit' && !isGroupStudyLoading && editDefaultValues && (
src/features/phone-verification/model/use-phone-auth-mutation.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/features/phone-verification/model/store.ts`:
- Around line 24-26: The TypeScript error comes from implicit any for
phoneNumber, verifiedAt, memberId when using the persist middleware; fix it by
declaring an explicit state type and/or asserting the initial null values' types
for the persisted slice: define a PhoneVerificationState interface (or type)
with phoneNumber: string | null, verifiedAt: string | null (or Date | null as
appropriate), memberId: string | null and use that type as the generic for your
store/persist call, or alternatively cast the initial values like phoneNumber as
string | null, verifiedAt as string | null, memberId as string | null so
TypeScript can infer correct types for the persist middleware and eliminate the
implicit any errors.
In `@src/features/study/group/ui/group-study-form-modal.tsx`:
- Around line 262-271: When in edit mode the code only renders GroupStudyForm
when editDefaultValues exists, causing an empty modal if isGroupStudyLoading is
false and editDefaultValues is null; add a fallback branch for mode === 'edit'
&& !isGroupStudyLoading && !editDefaultValues that renders a Modal.Body with an
error/empty state message and a retry action (e.g., a Retry button that calls
your existing refetch function or a new handler like refetchGroupStudy) and/or a
close option so the user isn’t left with a blank modal; update the conditional
rendering around isGroupStudyLoading/editDefaultValues/GroupStudyForm and wire
the retry to the appropriate handler (or expose a new onRetry prop) instead of
leaving the modal empty.
| phoneNumber: null, | ||
| verifiedAt: null, | ||
| memberId: null, |
There was a problem hiding this comment.
TypeScript 타입 추론 오류 수정 필요
정적 분석 도구에서 phoneNumber, verifiedAt, memberId 속성에 암시적 any 타입 오류가 발생합니다. persist 미들웨어와 함께 사용할 때 TypeScript가 제네릭 타입에서 null 값의 타입을 올바르게 추론하지 못하는 경우가 있습니다.
🛠️ 명시적 타입 단언 추가 제안
isVerified: false,
- phoneNumber: null,
- verifiedAt: null,
- memberId: null,
+ phoneNumber: null as string | null,
+ verifiedAt: null as string | null,
+ memberId: null as number | null,
hasHydrated: false,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| phoneNumber: null, | |
| verifiedAt: null, | |
| memberId: null, | |
| isVerified: false, | |
| phoneNumber: null as string | null, | |
| verifiedAt: null as string | null, | |
| memberId: null as number | null, | |
| hasHydrated: false, |
🧰 Tools
🪛 GitHub Check: typecheck
[failure] 26-26:
Object literal's property 'memberId' implicitly has an 'any' type.
[failure] 25-25:
Object literal's property 'verifiedAt' implicitly has an 'any' type.
[failure] 24-24:
Object literal's property 'phoneNumber' implicitly has an 'any' type.
🤖 Prompt for AI Agents
In `@src/features/phone-verification/model/store.ts` around lines 24 - 26, The
TypeScript error comes from implicit any for phoneNumber, verifiedAt, memberId
when using the persist middleware; fix it by declaring an explicit state type
and/or asserting the initial null values' types for the persisted slice: define
a PhoneVerificationState interface (or type) with phoneNumber: string | null,
verifiedAt: string | null (or Date | null as appropriate), memberId: string |
null and use that type as the generic for your store/persist call, or
alternatively cast the initial values like phoneNumber as string | null,
verifiedAt as string | null, memberId as string | null so TypeScript can infer
correct types for the persist middleware and eliminate the implicit any errors.
| {mode === 'edit' && isGroupStudyLoading && ( | ||
| <Modal.Body className="font-designer-16m text-text-subtle py-800 text-center"> | ||
| 스터디 정보를 불러오는 중입니다... | ||
| </Modal.Body> | ||
| )} | ||
| {mode === 'edit' && !isGroupStudyLoading && editDefaultValues && ( | ||
| <GroupStudyForm | ||
| defaultValues={editDefaultValues} | ||
| onSubmit={handleSubmitForm} | ||
| /> |
There was a problem hiding this comment.
편집 모드에서 데이터 미존재 시 빈 화면 상태가 발생합니다.
isGroupStudyLoading이 false인데 editDefaultValues가 null이면 모달 바디가 비어 보입니다(예: 조회 실패/데이터 없음). 에러/빈 상태 메시지 또는 재시도 UI를 추가해 주세요.
🤖 Prompt for AI Agents
In `@src/features/study/group/ui/group-study-form-modal.tsx` around lines 262 -
271, When in edit mode the code only renders GroupStudyForm when
editDefaultValues exists, causing an empty modal if isGroupStudyLoading is false
and editDefaultValues is null; add a fallback branch for mode === 'edit' &&
!isGroupStudyLoading && !editDefaultValues that renders a Modal.Body with an
error/empty state message and a retry action (e.g., a Retry button that calls
your existing refetch function or a new handler like refetchGroupStudy) and/or a
close option so the user isn’t left with a blank modal; update the conditional
rendering around isGroupStudyLoading/editDefaultValues/GroupStudyForm and wire
the retry to the appropriate handler (or expose a new onRetry prop) instead of
leaving the modal empty.
- 서버 레이아웃의 Clarity.init()를 클라이언트 전용 컴포넌트로 이동 - service/landing 레이아웃에 ClarityInit 적용 - edit 모드에서 데이터 로딩 전 GroupStudyForm 마운트 방지 - phone verification store의 persist partialize/onRehydrateStorage 타입 명시 - 전화번호 인증 동기화 시 memberId 저장/반영 로직 보강 fix: 타입체크
4ba143a to
b90519e
Compare
🌱 연관된 이슈
☘️ 작업 내용
🍀 참고사항
스크린샷 (선택)
Summary by CodeRabbit
새로운 기능
버그 수정
리팩토링